<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>闭包</title>
	<!--<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>-->
</head>
<body>
<script>
	//在JavaScript中函数不仅可以当作参数，而且可以当作返回值
	//可以当作返回值的这种形式，称作闭包
	//闭包就是携带状态的函数
	function sum(arr) {
		return arr.reduce(function (x,y) {
			return x + y;
		})
	}

	let arr = [1,2,3,4,5];
	var result = sum(arr);
	console.log("sum:" + result);

	//上面的形式会在函数调用的时候立刻返回，但是我们想根据需要再进行计算;
	//闭包
	//在函数lazy_sum 中定义了函数sum，
	//sum可以引用外部函数lazy_sum的参数和局部变量
	//当lazy_sum 返回sum时，相关的参数和变量都保存在了
	//返回的函数中，这种程序结构称作闭包

	function lazy_sum(arr) {
		var sum = function () {
			return arr.reduce(function (x,y) {
				return x + y;
			})
		}
		return sum;
	}

	let lazy_sum_result = lazy_sum([1,2,3,4,5]);
	console.log("lazy_sum_result:" + lazy_sum_result);
	console.log("lazy_sum_result():" + lazy_sum_result())

	//lazy_sum 每次都会返回一个新的函数
	let f1 = lazy_sum([1,2,3,4,5]);
	let f2 = lazy_sum([1,2,3,4,5]);
	console.log("f1 === f2 : " + f1 === f2 )

	// function lazy_sum(arr) {
	// 	var sum = function () {
	// 		return arr.reduce(function (x, y) {
	// 			return x + y;
	// 		});
	// 	}
	// 	return sum;
	// }
	// var f = lazy_sum([1, 2, 3, 4, 5]); // function sum()
	// console.log(f());

	function count() {
		var arr = [];
		for (var i=1; i<=3; i++) {
			arr.push(function () {
				return i*i;
			})
		}
		return arr;
	}
	var resultCount = count();
	var rs1 = resultCount[0];
	var rs2 = resultCount[1];
	var rs3 = resultCount[2];
	//返回的函数引用了变量i，但它并非立刻执行，等三个函数返回时候i变成了4
	console.log("rs1():" + rs1()); //16
	console.log("rs2():" + rs2()); //16
	console.log("rs3():" + rs3()); //16

	//使用闭包的过程中，不能使用任何循环变量，或者后续发生变化的变量
	//使用闭包的过程中，不能使用任何循环变量，或者后续发生变化的变量
	//使用闭包的过程中，不能使用任何循环变量，或者后续发生变化的变量
	//重要的事情说三遍

	//如果一定要使用循环变量，就必须再创建一个函数，用函数的参数绑定循环
	//变量的值，那么循环变量再怎么变化，已绑定的值都不会变化
	function countCircle() {
		var arr = [];
		for (var i=1; i<=3; i++) {
			//push中的 funtion () {}();代表匿名函数并立刻执行
			arr.push((function (n) {
				//循环变量的当前值i与参数n绑定
				//函数也是一种变量
				//函数也是一种变量
				//函数也是一种变量
				return function () {
					return n*n;
				}
			})(i))
		}
		return arr;
	}
	var resultCircle = countCircle();
	var rc1 = resultCircle[0];
	var rc2 = resultCircle[1];
	var rc3 = resultCircle[2];
	//函数调用
	console.log("rc1():" + rc1());
	console.log("rc2():" + rc2());
	console.log("rc3():" + rc3());
	//创建并调用匿名函数
	// function f(x) {
	// 	console.log("x:" + x);
	// }(10);
	//由于语法问题会报出SyntaxError错误,调用的时候要用()将函数括起来
	(function f(x) {
		console.log("x:" + x);
	})(10);

	//闭包作用1
	//在面向对象的语言中java,c++ 要在对象内部封装一个私有变量可以用private修饰
	//在没有class机制的，只有函数的语言中，借住闭包可以实现同样的功能；
	//利用javascript创建一个计数器
	function create_counter(initial) {
		var x = initial || 0;
		return {
			inc: function () {
				x += 1;
				return x;
			}
		}
	}
	var c1 = create_counter();
	console.log("c1.inc():" + c1.inc());
	console.log("c1.inc():" + c1.inc());
	console.log("c1.inc():" + c1.inc());

	var c2 = create_counter(10);
	console.log("c2.inc():" + c2.inc());
	console.log("c2.inc():" + c2.inc());
	console.log("c2.inc():" + c2.inc());

	//闭包作用2
	//利用闭包创建一个函数
	function make_pow(n) {
		return function (x) {
			return Math.pow(x,n);
		}
	}
	var pow2 = make_pow(2);
	var pow3 = make_pow(3);
	console.log("pow2(3):" + pow2(3));
	console.log("pow3(2):" + pow3(2))

	add : function (x,y) {
		return x + y;
	}
	console.log("add():" + add(x,y));
</script>
</body>
</html>